package org.smartboot.flow.core.builder;


import org.smartboot.flow.core.Adapter;
import org.smartboot.flow.core.Condition;
import org.smartboot.flow.core.Pipeline;
import org.smartboot.flow.core.component.Component;
import org.smartboot.flow.core.component.PipelineComponent;
import org.smartboot.flow.core.executable.Executable;
import org.smartboot.flow.core.executable.ExecutableAdapter;
import org.smartboot.flow.core.util.AssertUtil;

import java.util.ArrayList;
import java.util.List;

/**
 * @author qinluo
 * @date 2022-11-11 16:18:38
 * @since 1.0.0
 */
public class PipelineBuilder<T, S> {

    /**
     * Parent process builder.
     */
    private final PipelineBuilder<T, S> parent;
    private String name;
    private final List<Component<T, S>> components = new ArrayList<>();

    public PipelineBuilder(PipelineBuilder<T, S> parent, String name) {
        this.parent = parent;
        this.name = name;
    }

    public PipelineBuilder(String name) {
        this(null, name);
    }

    public PipelineBuilder<T, S> name(String name) {
        AssertUtil.notBlank(name, "must not be null");
        this.name = name;
        return this;
    }

    public PipelineBuilder<T, S> next(Component<T, S> component) {
        AssertUtil.notNull(component, "must not be null");
        this.components.add(component);
        return this;
    }

    public PipelineBuilder<T, S> next(Executable<T, S> executable) {
        AssertUtil.notNull(executable, "must not be null");
        Component<T, S> adapter = new ExecutableAdapter<>(executable);
        this.components.add(adapter);
        return this;
    }

    public IfComponentBuilder<T, S> next(Condition<T, S> condition) {
        AssertUtil.notNull(condition, "must not be null");
        return new IfComponentBuilder<>(this, condition);
    }

    public <P, Q> PipelineBuilder<T,S> adapter(Adapter<T,S, P, Q> adapter, Component<P, Q> component) {
        return this.next(new AdapterBuilder<>(adapter, component).build());
    }

    public ChooseBuilder<T, S> choose(Condition<T, S> condition) {
        AssertUtil.notNull(condition, "must not be null");
        return new ChooseBuilder<>(this, condition);
    }

    public PipelineBuilder<T, S> pipeline(String name) {
        AssertUtil.notBlank(name, "must not be null");
        return new PipelineBuilder<>(this, name);
    }


    @SuppressWarnings("UnusedReturnValue")
    public PipelineBuilder<T, S> end() {
        // End subprocess build, parent must not be null.
        AssertUtil.notNull(parent, "Only subprocess call this method allowed");

        Pipeline<T, S> pipeline = build();
        PipelineComponent<T, S> pipelineComponent = new PipelineComponent<>(pipeline);
        pipelineComponent.setName("anonymous@" + name);

        parent.next(pipelineComponent);
        return parent;
    }

    public Pipeline<T, S> build() {
        AssertUtil.notBlank(name, "pipeline's name is required");

        Pipeline<T, S> pipeline = new Pipeline<>();
        pipeline.setComponents(this.components);
        pipeline.setName(name);
        return pipeline;
    }

}
